package grex;

/**
 * <p>Title: </p>
 * <p>Description: Simplyfies a rule using grex</p>
 * <p>Copyright: Copyright (c) 2003</p>
 * <p>Company: </p>
 * @author not attributable
 * @version 1.0
 */
import grex.ErrorManagement.ErrorManager;
import grex.Data.Prediction;
import grex.Data.PredictionContainer;
import grex.Nodes.GeneException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;

public class Simplyfier extends Thread{
  private GP gp;
  private World world;
  private GP bestShort;
  private boolean interrupted=false;
  private ArrayList <Double> trainPred;
  private CopyOnWriteArrayList <SimplificationListener> listeners;
  private ArrayList <GP> batchGp = new ArrayList();
  private PredictionContainer orgValues;
  int currentGeneration,currentBatch;
  Options options;
  Thread t;
  public Simplyfier(GP gp, World world,CopyOnWriteArrayList<SimplificationListener> listeners) throws BNFException, GeneException,
      IOException {
      options=world.getEnvironment().getOptions();

    this.listeners=listeners;

    this.gp = (GP)gp.clone();
    this.world = world;

    setGPpredictionInEnvironment();
  }
  
  public Simplyfier(World world){
      this.world=world;
  }
  public void setGP(GP gp) throws BNFException, GeneException{
      this.gp=gp;
      trainPred=gp.getGPTrainPredictionAsArrayList();
      setGPpredictionInEnvironment();
  }
  
  public void run(){
        simplify();
  }
  
  public void simplify() {
        try {
            bestShort = gp;
            fireSimplificationStarted();
            options.setRUN_SIMPLIFICATION(true);
            options.setCREATION_TYPE(0);
            options.setELITISM(true);
            int orgTournamnetmembers = options.getNR_OF_TOURNAMNET_MEMBERS();
            options.setNR_OF_TOURNAMNET_MEMBERS(2);
                for (currentBatch = 0; currentBatch < options.getSIMPLIFICATION_BATCH() && !this.isInterrupted();currentBatch++) {
                world.createNewPopulation(options.getSIMPLIFICATION_POPULATION(), gp);
                for (currentGeneration = 0; currentGeneration <= options.getSIMPLIFICATION_GENERATIONS() && !this.isInterrupted(); currentGeneration++) {
                    world.evolve(1);
                    bestShort = world.getBestEverGP();
                    fireSimplificationGenerationTerminated();
                }
                batchGp.add(world.getBestEverGP());
            }
            options.setRUN_SIMPLIFICATION(false);
            options.setNR_OF_TOURNAMNET_MEMBERS(orgTournamnetmembers);
            bestShort = getBestGPinBatch();
            reSetTargetInEnvironment();
            fireSimplificationTerminated();
        }

        catch (Exception e) {
            ErrorManager.getInstance().reportError(e);
        }
    }
  
  public int getCurrentGeneration(){
      return currentGeneration+1;
  }
  public int getCurrentBatch(){
      return currentBatch+1;
  }
  public int getRemainingGenerations(){
      return options.getBATCH_SIZE()*options.getGENERATIONS() - getCurrentBatch()*getCurrentGeneration();
  }
  public GP  getSimplyfiedGP(){
    return bestShort;
  }
  public double getAverageFitness(){
      return world.getAvgPopulationFitness();
  }
  public double getAverageLength(){
      return world.getAvgPopulationLength();
  }

  private synchronized void fireSimplificationGenerationTerminated(){
      if(listeners!=null){
          for(SimplificationListener l:listeners)
              l.simplificationGenerationTerminated(this);
      }
  }
  private synchronized void fireSimplificationTerminated(){
      if(listeners!=null){
          for(SimplificationListener l:listeners)
              l.simplifictionTerminated(this);
      }
  }
  private synchronized void fireSimplificationStarted(){
      if(listeners!=null){
          for(SimplificationListener l:listeners)
              l.simplificationStarted(this);
      }
  }
  
  private GP getBestGPinBatch(){      
      GP best=null;
      int batchNr = 0;
      int j = 1;
      for(GP current: batchGp){              
        if (best==null || best.getFitness() < current.getFitness()) {
          best = current;
          batchNr = j;
        }
        j++;
      }
      return best;
  }

  /*Changes the target variable in the invironment to the gp prediction*/
  public void setGPpredictionInEnvironment() throws GeneException {
    orgValues= gp.getPcTrain().clone();
    options.setTestFOLD(gp.getFold());
                //environment.initFold(fold);
//    PredictionContainer simpPc = gp.getPcTrain().clone();
 //   gp.setPcTrain(simpPc);
    for(Prediction p: gp.getPcTrain().values())
        p.setTargetValue(p.getPrediction());

  }
  public void reSetTargetInEnvironment() throws GeneException {
      //funkar troligen inte då arrayen ändras (call by ref)
    gp.setPcTrain(orgValues);
    //double[][] train = world.getEnvironment().getTrainData();
/*
     for(int i=0;i<train.length;i++){
       train[i][gp.getEnvironment().getTargetColumnIndex()] = gp.getPcTrain().get(train[i]).getTargetValue();
     }*/
  }


}
